program SplitScreen;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  SampleCommon,
  DIrr in '..\..\DSource\DIrr.pas',
  DIrr.GUI in '..\..\DSource\DIrr.GUI.pas',
  DIrr.Color in '..\..\DSource\DIrr.Color.pas',
  DIrr.Rect in '..\..\DSource\DIrr.Rect.pas',
  DIrr.Event in '..\..\DSource\DIrr.Event.pas',
  DIrr.Vector2D in '..\..\DSource\DIrr.Vector2D.pas',
  DIrr.Vector3D in '..\..\DSource\DIrr.Vector3D.pas',
  DIrr.Video in '..\..\DSource\DIrr.Video.pas',
  DIrr.Base in '..\..\DSource\DIrr.Base.pas',
  DIrr.Types in '..\..\DSource\DIrr.Types.pas',
  DIrr.Scene in '..\..\DSource\DIrr.Scene.pas',
  DIrr.Structs in '..\..\DSource\DIrr.Structs.pas',
  DIrr.Aabbox3D in '..\..\DSource\DIrr.Aabbox3D.pas',
  DIrr.Matrix4 in '..\..\DSource\DIrr.Matrix4.pas',
  DIrr.Texture in '..\..\DSource\DIrr.Texture.pas',
  DIrr.Dimension2d in '..\..\DSource\DIrr.Dimension2D.pas',
  DIrr.Collections in '..\..\DSource\DIrr.Collections.pas',
  DIrr.ConcreteMesh in '..\..\DSource\DIrr.ConcreteMesh.pas',
  DIrr.Quake3 in '..\..\DSource\DIrr.Quake3.pas',
  DIrr.Buffer in '..\..\DSource\DIrr.Buffer.pas',
  DIrr.IO in '..\..\DSource\DIrr.IO.pas',
  DIrr.Consts in '..\..\DSource\DIrr.Consts.pas';

  (*
  Now we'll define the resolution in a constant for use in
  initializing the device and setting up the viewport. In addition
  we set up a global variable saying splitscreen is active or not.
  *)
  //Resolution
const
  RESX: Integer = 800;
  RESY: Integer = 600;
  FULLSCREEN: Boolean = False;

  //Use SplitScreen?
var
  SplitScrn: Boolean = True;

  (*
  Now we need four pointers to our cameras which are created later:
  *)
  //cameras
  Camera: array[0..3] of TCameraSceneNode = (nil, nil, nil, nil);

  (*
  In our event-receiver we switch the SplitScreen-variable,
  whenever the user press the S-key. All other events are sent
  to the FPS camera.
  *)
type
  TMyEventReceiver = class(TEventReceiver)
  public
    function OnEvent(const Event: Sevent): Boolean; override;
  end;

{ TMyEventReceiver }

function TMyEventReceiver.OnEvent(const Event: Sevent): Boolean;
begin
   //Key S enables/disables SplitScreen
   if (Event.EventType = EET_KEY_INPUT_EVENT) and
    (Event.KeyInput.Key = KEY_KEY_S) and (Event.KeyInput.PressedDown) then
   begin
     SplitScrn := not SplitScrn;
     Result := True;
     Exit;
   end;

   //Send all other events to camera4
   if Assigned(Camera[3]) then
     Result := Camera[3].OnEvent(Event)
   else
     Result := False;
end;

(*
Ok, now the main-function:
First, we initialize the device, get the SourceManager and
VideoDriver, load an animated mesh from .md2 and a map from
.pk3. Because that's old stuff, I won't explain every step.
Just take care of the maps position.
*)
var
  DriverType: E_DRIVER_TYPE;
  Receiver: TMyEventReceiver;
  Device: TIrrlichtDevice;
  Smgr: TSceneManager;
  Driver: TVideoDriver;
  Model, Map: TAnimatedMesh;
  ModelNode: TAnimatedMeshSceneNode;
  MapNode: TSceneNode;
  Texture: TTexture;
  LastFPS: Integer = -1;
  TmpStr: String;
begin
  try
    ReportMemoryLeaksOnShutdown := True;

    // ask user for driver
    DriverType := DriverChoiceConsole;
    if DriverType = EDT_COUNT then
    begin
      ExitCode := 1;
      Exit;
    end;
    //Instance of the EventReceiver
    Receiver := TMyEventReceiver.Create;

    //Initialise the engine
    Device := CreateDevice(DriverType, Dimension2DU(RESX, RESY), 32,
      FULLSCREEN, False, False, Receiver);

    if not Assigned(Device) then
    begin
      ExitCode := 1;
      Exit;
    end;

    Smgr := Device.GetSceneManager;
    Driver := Device.GetVideoDriver;

    //Load model
    Model := Smgr.GetMesh('..\..\media\sydney.md2');
    if not Assigned(model) then
    begin
      ExitCode := 1;
      Exit;
    end;
    ModelNode := Smgr.AddAnimatedMeshSceneNode(Model);

    //Load texture
    if Assigned(ModelNode) then
    begin
      Texture := Driver.GetTexture('..\..\media\sydney.bmp');
      ModelNode.SetMaterialTexture(0, Texture);
      ModelNode.SetMD2Animation(EMAT_RUN);
      //Disable lighting (we've got no light)
      ModelNode.SetMaterialFlag(EMF_LIGHTING, False);
    end;

    //Load map
    Device.GetFileSystem.AddFileArchive('..\..\media\map-20kdm2.pk3');
    Map := Smgr.GetMesh('20kdm2.bsp');
    if Assigned(Map) then
    begin
      MapNode := Smgr.AddOctreeSceneNode(Map.GetMesh(0));
      //Set position
      MapNode.SetPosition(Vector3DF(-850, -220, -850));
    end;

  (*
  Now we create our four cameras. One is looking at the model
  from the front, one from the top and one from the side. In
  addition there's a FPS-camera which can be controlled by the
  user.
  *)
    // Create 3 fixed and one user-controlled cameras
    //Front
    Camera[0] := Smgr.AddCameraSceneNode(nil, Vector3dF(50, 0, 0), Vector3dF(0, 0, 0));
    //Top
    Camera[1] := Smgr.AddCameraSceneNode(nil, Vector3dF(0, 50, 0), Vector3dF(0, 0, 0));
    //Left
    Camera[2] := Smgr.AddCameraSceneNode(nil, Vector3dF(0, 0, 50), Vector3dF(0, 0, 0));
    //User-controlled
    Camera[3] := Smgr.AddCameraSceneNodeFPS();
    // don't start at sydney's position
    if Assigned(Camera[3]) then
      Camera[3].SetPosition(Vector3dF(-50, 0, -50));

  (*
  Create a variable for counting the fps and hide the mouse:
  *)
    //Hide mouse
    Device.GetCursorControl.SetVisible(False);

  (*
  There wasn't much new stuff - till now!
  Only by defining four cameras, the game won't be splitscreen.
  To do this you need several steps:
    - Set the viewport to the whole screen
    - Begin a new scene (Clear screen)

    - The following 3 steps are repeated for every viewport in the splitscreen
      - Set the viewport to the area you wish
      - Activate the camera which should be "linked" with the viewport
      - Render all objects

    - If you have a GUI:
      - Set the viewport the whole screen
      - Display the GUI
    - End scene

  Sounds a little complicated, but you'll see it isn't:
  *)
    while Device.Run do
    begin
      //Set the viewpoint to the whole screen and begin scene
      Driver.SetViewPort(IrrRect(0, 0, RESX, RESY));
      Driver.BeginScene(True, True, IrrColor(100, 100, 100, 255));
      //If SplitScreen is used
      if SplitScrn then
      begin
        //Activate camera1
        Smgr.SetActiveCamera(Camera[0]);
        //Set viewpoint to the first quarter (left top)
        Driver.SetViewPort(IrrRect(0, 0, RESX div 2, ResY div 2));
        //Draw scene
        Smgr.DrawAll();
        //Activate camera2
        Smgr.SetActiveCamera(Camera[1]);
        //Set viewpoint to the second quarter (right top)
        Driver.SetViewPort(IrrRect(RESX div 2, 0, RESX, RESY div 2));
        //Draw scene
        Smgr.DrawAll();
        //Activate camera3
        Smgr.SetActiveCamera(Camera[2]);
        //Set viewpoint to the third quarter (left bottom)
        Driver.SetViewPort(IrrRect(0, RESY div 2, RESX div 2, ResY));
        //Draw scene
        Smgr.DrawAll();
        //Set viewport the last quarter (right bottom)
        Driver.SetViewPort(IrrRect(RESX div 2, RESY div 2, RESX, RESY));
      end;
      //Activate camera4
      Smgr.SetActiveCamera(Camera[3]);
      //Draw scene
      Smgr.DrawAll;

      Driver.EndScene;

      (*
      As you can probably see, the image is rendered for every
      viewport separately. That means, that you'll loose much performance.
      Ok, if you're asking "How do I have to set the viewport
      to get this or that screen?", don't panic. It's really
      easy: In the rect-function you define 4 coordinates:
      - X-coordinate of the corner left top
      - Y-coordinate of the corner left top
      - X-coordinate of the corner right bottom
      - Y-coordinate of the corner right bottom

      That means, if you want to split the screen into 2 viewports
      you would give the following coordinates:
      - 1st viewport: 0,0,ResX/2,ResY
      - 2nd viewport: ResX/2,0,ResX,ResY

      If you didn't fully understand, just play around with the example
      to check out what happens.

      Now we just view the current fps and shut down the engine,
      when the user wants to:
      *)
      //Get and show fps
      if Driver.GetFPS <> LastFPS then
      begin
        LastFPS := Driver.GetFPS;
        TmpStr := 'Irrlicht SplitScreen-Example (FPS: ' + IntToStr(LastFPS) + ')';
        Device.SetWindowCaption(PChar(TmpStr));
      end;
    end;

    Device.Drop;
    Receiver.Free;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
